// The server_example shows how to write write a game server with Rpc service and event/frame processes.

package main

import (
	"flag"
	"fmt"
	"github.com/golang/protobuf/proto"
	"go.uber.org/zap"
	"gse/exmaples/protos"
	"gse/server"
	"gse/server/tcp"
	"gse/server/udp"
	"net/http"
	_ "net/http/pprof"
	"os"
	"strings"
	"time"
)

var logger *zap.Logger

var asyncMode bool
var reqN int
var concurrency int
var srv server.GSEServer
var transportProtocol string
var pg *server.EventPushGroup

func main() {
	flagset := flag.NewFlagSet("parameters", flag.ExitOnError)
	var sAddr string
	flagset.StringVar(&sAddr, "s", "", "server address, e.g. 127.0.0.1:3131")
	flagset.StringVar(&transportProtocol, "t", "udp", "transport type, \"tcp\" or \"udp\"")
	flagset.BoolVar(&asyncMode, "a", false, "requests in asynchronously.")
	flagset.IntVar(&reqN, "n", 10, "number of requests to be sent to each client.")
	flagset.IntVar(&concurrency, "c", 1, "number of concurrent routines for performing requests.")
	flagset.Parse(os.Args[1:])

	go http.ListenAndServe("0.0.0.0:6364", nil)
	transportProtocol = strings.ToLower(transportProtocol)
	switch transportProtocol {
	case "tcp":
	case "udp":
	default:
		fmt.Printf(`Un-supported transport "%v"`+"\r\n", transportProtocol)
		os.Exit(1)
	}
	var sesConfig *server.SessionConfig

	if transportProtocol == "udp" {
		cfg := udp.NewDefaultUDPServerConfig()
		sesConfig = cfg.SesConfig
		if sAddr != "" {
			cfg.BindAddress = sAddr
		}
		cfg.SesConfig.ReqWaitTimeout = time.Second * 6
		cfg.KeepAlive = 3000
		srv = udp.NewUDPServer(cfg)
	} else {
		cfg := tcp.NewDefaultTCPServerConfig()
		sesConfig = cfg.SesConfig
		if sAddr != "" {
			cfg.BindAddress = sAddr
		}
		srv = tcp.NewTCPServer(cfg)
	}

	logger = server.GetLogger()

	srv.RegisterEvent("sync_arena_frames", &server.EventConfig{20})
	sesConfig.RegisterHook(server.AfterCreate(sessionCreateHook))
	srv.RegisterServiceHandler("UpdateHeroStatus", HanldeUpdateHeroStatus, &protos.HeroStatusUpdateRequest{})
	pg = srv.GetEventPusher().CreateGroup("battle field 1")
	go GroupPush()
	srv.Serve()
}

func GroupPush() {
	// simulate event push
	timer := time.NewTicker(time.Millisecond * 20)
	var i uint32
	for {
		t := <-timer.C
		m := new(protos.BattleEvent)
		m.RoleId = 10021
		i++
		m.SequenceNo = i
		m.Command = fmt.Sprintf("组消息, 时间: %v ", t.String())
		pg.QueueEvent("sync_arena_frames", m)
	}
}

func HanldeUpdateHeroStatus(status proto.Message) proto.Message {
	rsp := new(protos.HeroStatusUpdateResponse)
	rsp.Status = protos.HeroStatusUpdateResponse_Success
	rsp.Desc = fmt.Sprintf("[%v] 数据已记录.", time.Now())

	return rsp
}

func sessionCreateHook(ses *server.Session, args ...interface{}) {
	var callback server.RequestCallback
	if asyncMode {
		callback = ReqCallback
	} else {
		callback = nil
	}
	logger.Debug(fmt.Sprintf("created session(%v): %+v ", ses.GetRemoteAddr(), *ses))
	pg.RegSession(ses)
	logger.Debug(fmt.Sprintf("register session(%v): %+v  to group %v", ses.GetRemoteAddr(), *ses, pg.Name))

	go func() {
		<-time.After(time.Second)
		subReg, err := ses.SubEvent("client_battle_frames", &protos.BattleEvent{})
		if err != nil {
			panic(err)
		}
		ch := subReg.EventChan()
		for {
			p := <-ch
			logger.Debug("got push messages", zap.String("msg", fmt.Sprintf("%+v", p.GetMessages())))
		}
	}()

	for n := concurrency; n > 0; n-- {
		// simulate event push
		go func() {
			pusher := srv.GetEventPusher()
			timer := time.NewTicker(time.Millisecond * 30)
			var i uint32
			for {
				t := <-timer.C
				m := new(protos.BattleEvent)
				m.RoleId = 10021
				i++
				m.SequenceNo = i
				m.Command = fmt.Sprintf("会话消息(%v), 时间: %v", ses.GetRemoteAddr(), t.String())
				pusher.QueueEventToSession("sync_arena_frames", ses, m)
			}
		}()

		go func() {
			for i := reqN; i > 0; i-- {
				// sync request.
				reqParam := &protos.BattleEvent{
					RoleId:     1001,
					SequenceNo: uint32(i),
					Command:    "spell magic 1",
				}
				reqRsp := &protos.HeroReportStatusResponse{}
				err := ses.Request("Hero.ReportStatus", reqParam, reqRsp, callback)
				if err != nil {
					logger.Debug(fmt.Sprintln("****************", ses.GetRemoteAddr(), "EEEEEE", err, "EEEEEE"))
				} else {
					logger.Debug(fmt.Sprintln("****************", ses.GetRemoteAddr(), "Hero.ReportStatus got response:", reqRsp))
				}
			}
		}()
	}
}

func ReqCallback(param proto.Message, err error) {
	logger.Debug(fmt.Sprintf("received async response, %+v, ERR: %v", param, err))
}
